/*     */ package com.zimbra.cs.session;
/*     */ 
/*     */ import com.google.common.annotations.VisibleForTesting;
/*     */ import com.google.common.util.concurrent.ThreadFactoryBuilder;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collection;
/*     */ import java.util.Collections;
/*     */ import java.util.HashMap;
/*     */ import java.util.Iterator;
/*     */ import java.util.LinkedHashMap;
/*     */ import java.util.LinkedHashSet;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Map.Entry;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.ExecutorService;
/*     */ import java.util.concurrent.Executors;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ final class SessionMap
/*     */ {
/*  45 */   private static final ExecutorService SWEEPER = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("SessionSweeper").setDaemon(true).build());
/*     */   
/*     */   private final Session.Type type;
/*     */   
/*  49 */   private final Map<String, AccountSessionMap> accountSessionMap = new LinkedHashMap(100);
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*  58 */   private final Map<Long, Set<Session>> sessionAccessSet = new HashMap();
/*  59 */   private int totalActiveSessions = 0;
/*     */   
/*     */   SessionMap(Session.Type type)
/*     */   {
/*  63 */     this.type = type;
/*     */   }
/*     */   
/*     */   public Session.Type getType() {
/*  67 */     return this.type;
/*     */   }
/*     */   
/*     */   public synchronized int totalActiveAccounts()
/*     */   {
/*  72 */     return this.accountSessionMap.size();
/*     */   }
/*     */   
/*     */   public synchronized int totalActiveSessions()
/*     */   {
/*  77 */     return this.totalActiveSessions;
/*     */   }
/*     */   
/*     */ 
/*     */   public synchronized int countActiveSessions(String accountId)
/*     */   {
/*  83 */     AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/*  84 */     if (acctMap != null) {
/*  85 */       return this.accountSessionMap.size();
/*     */     }
/*  87 */     return 0;
/*     */   }
/*     */   
/*     */   public synchronized Collection<AccountSessionMap> activeAccounts()
/*     */   {
/*  92 */     return Collections.unmodifiableCollection(this.accountSessionMap.values());
/*     */   }
/*     */   
/*     */   public synchronized Collection<Session> get(String accountId) {
/*  96 */     AccountSessionMap m = (AccountSessionMap)this.accountSessionMap.get(accountId);
/*  97 */     return m == null ? null : Collections.unmodifiableCollection(m.values());
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized Session get(String accountId, String sessionId)
/*     */   {
/* 105 */     AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/* 106 */     if (acctMap != null) {
/* 107 */       Session session = (Session)acctMap.get(sessionId);
/* 108 */       if (session != null) {
/* 109 */         updateAccessTime(session);
/*     */       }
/* 111 */       return session;
/*     */     }
/* 113 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public synchronized Session remove(String accountId, String sessionId)
/*     */   {
/* 122 */     AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/* 123 */     if (acctMap != null) {
/* 124 */       Session removed = (Session)acctMap.remove(sessionId);
/* 125 */       if (removed != null) {
/* 126 */         long removedTimeout = removed.getSessionIdleLifetime();
/* 127 */         Set<Session> set = getSessionAccessSet(Long.valueOf(removedTimeout));
/* 128 */         assert (set.contains(removed));
/* 129 */         set.remove(removed);
/* 130 */         this.totalActiveSessions -= 1;
/* 131 */         if (set.isEmpty()) {
/* 132 */           this.sessionAccessSet.remove(Long.valueOf(removedTimeout));
/*     */         }
/* 134 */         if (acctMap.isEmpty()) {
/* 135 */           this.accountSessionMap.remove(accountId);
/*     */         }
/*     */       }
/* 138 */       return removed;
/*     */     }
/* 140 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   public void putAndPrune(String accountId, String sessionId, Session session, int maxSessionsPerAcct)
/*     */   {
/* 152 */     synchronized (this) {
/* 153 */       assert (session != null);
/* 154 */       put(accountId, sessionId, session);
/*     */       
/* 156 */       AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/* 157 */       int iterations = 0;
/* 158 */       while ((acctMap != null) && (acctMap.size() > maxSessionsPerAcct)) {
/* 159 */         iterations++;
/* 160 */         long leastRecent = Long.MAX_VALUE;
/* 161 */         String leastRecentId = null;
/*     */         
/* 163 */         for (Map.Entry<String, Session> entry : acctMap.entrySet()) {
/* 164 */           Session s = (Session)entry.getValue();
/* 165 */           if (s.getLastAccessTime() < leastRecent) {
/* 166 */             leastRecent = s.getLastAccessTime();
/* 167 */             leastRecentId = (String)entry.getKey();
/*     */           }
/*     */         }
/* 170 */         assert (leastRecentId != null);
/*     */         
/* 172 */         int prevSize = acctMap.size();
/* 173 */         final Session removed = remove(accountId, leastRecentId);
/* 174 */         if (removed != null) {
/* 175 */           ZimbraLog.session.info("Too many %s sessions, closing %s", new Object[] { session.getType(), removed });
/*     */           
/* 177 */           SWEEPER.submit(new Runnable()
/*     */           {
/*     */             public void run() {
/* 180 */               removed.doCleanup();
/*     */             }
/*     */           });
/*     */         }
/*     */         
/*     */ 
/* 186 */         acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/*     */         
/* 188 */         assert ((acctMap == null) || (acctMap.size() < prevSize));
/* 189 */         if ((acctMap.size() > maxSessionsPerAcct) || (acctMap.size() >= prevSize)) {
/* 190 */           ZimbraLog.session.warn("Problem in SessionMap.putAndPrune(%d): accountId: %s session: %s  maxPerAcct: %d prevSize: %d finishSize: %d leastRecentTime: %d leastRecentId: %s removed: %s", new Object[] { Integer.valueOf(iterations), accountId, sessionId, Integer.valueOf(maxSessionsPerAcct), Integer.valueOf(prevSize), Integer.valueOf(acctMap.size()), Long.valueOf(leastRecent), leastRecentId, removed });
/*     */           
/* 192 */           StringBuilder sb = new StringBuilder("SessionMap for account ");
/* 193 */           sb.append(accountId).append(" contains: ");
/* 194 */           for (Map.Entry<String, Session> entry : acctMap.entrySet()) {
/* 195 */             sb.append("(").append((String)entry.getKey()).append(",").append(((Session)entry.getValue()).toString());
/* 196 */             sb.append(" time=").append(((Session)entry.getValue()).getLastAccessTime()).append(") ");
/*     */           }
/* 198 */           ZimbraLog.session.warn(sb.toString());
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   @VisibleForTesting
/*     */   synchronized Session put(String accountId, String sessionId, Session session)
/*     */   {
/* 211 */     assert (session != null);
/* 212 */     AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(accountId);
/* 213 */     if (acctMap == null) {
/* 214 */       acctMap = new AccountSessionMap();
/* 215 */       this.accountSessionMap.put(accountId, acctMap);
/*     */     }
/* 217 */     assert (!acctMap.containsKey(sessionId));
/* 218 */     this.totalActiveSessions += 1;
/* 219 */     Session oldValue = (Session)acctMap.put(sessionId, session);
/* 220 */     updateAccessTime(session);
/* 221 */     return oldValue;
/*     */   }
/*     */   
/*     */   private synchronized void updateAccessTime(Session session) {
/* 225 */     Set<Session> set = getSessionAccessSet(Long.valueOf(session.getSessionIdleLifetime()));
/* 226 */     set.remove(session);
/* 227 */     session.sessionCacheSetLastAccessTime();
/* 228 */     set.add(session);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   synchronized List<Session> copySessionList()
/*     */   {
/* 235 */     List<Session> toRet = new ArrayList(this.totalActiveSessions);
/* 236 */     for (Set<Session> set : this.sessionAccessSet.values()) {
/* 237 */       toRet.addAll(set);
/*     */     }
/* 239 */     return toRet;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   synchronized List<Session> pruneSessionsByTime(long cutoffTime)
/*     */   {
/* 251 */     return pruneSessionsInternal(false, cutoffTime);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   synchronized List<Session> pruneAllSessions()
/*     */   {
/* 263 */     return pruneSessionsInternal(true, -1L);
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   synchronized List<Session> pruneIdleSessions()
/*     */   {
/* 275 */     return pruneSessionsInternal(false, -1L);
/*     */   }
/*     */   
/*     */   private synchronized List<Session> pruneSessionsInternal(boolean all, long cutoffIn) {
/* 279 */     List<Session> toRet = new ArrayList();
/* 280 */     long now = System.currentTimeMillis();
/*     */     
/* 282 */     for (Map.Entry<Long, Set<Session>> entry : this.sessionAccessSet.entrySet()) {
/* 283 */       cutoff = cutoffIn;
/* 284 */       if (all) {
/* 285 */         cutoff = Long.MAX_VALUE;
/*     */       }
/* 287 */       else if (cutoff == -1L) {
/* 288 */         cutoff = now - ((Long)entry.getKey()).longValue();
/*     */       }
/*     */       
/*     */ 
/* 292 */       for (iter = ((Set)entry.getValue()).iterator(); iter.hasNext();) {
/* 293 */         Session s = (Session)iter.next();
/* 294 */         if (s.accessedAfter(cutoff)) break;
/* 295 */         toRet.add(s);
/* 296 */         iter.remove();
/* 297 */         this.totalActiveSessions -= 1;
/* 298 */         AccountSessionMap acctMap = (AccountSessionMap)this.accountSessionMap.get(s.getAuthenticatedAccountId());
/* 299 */         Session removed = (Session)acctMap.remove(s.getSessionId());
/* 300 */         if ((removed != null) && 
/* 301 */           (acctMap.isEmpty())) {
/* 302 */           this.accountSessionMap.remove(s.getAuthenticatedAccountId());
/*     */         }
/*     */       }
/*     */     }
/*     */     
/*     */     long cutoff;
/*     */     
/*     */     Iterator<Session> iter;
/*     */     
/* 311 */     return toRet;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private synchronized Set<Session> getSessionAccessSet(Long timeout)
/*     */   {
/* 320 */     Set<Session> toRet = (Set)this.sessionAccessSet.get(timeout);
/* 321 */     if (toRet == null) {
/* 322 */       toRet = new LinkedHashSet();
/* 323 */       this.sessionAccessSet.put(timeout, toRet);
/*     */     }
/* 325 */     return toRet;
/*     */   }
/*     */   
/*     */   static final class AccountSessionMap extends HashMap<String, Session>
/*     */   {
/*     */     private static final long serialVersionUID = -8141746787729464753L;
/*     */     
/*     */     AccountSessionMap() {
/* 333 */       super();
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/session/SessionMap.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */